using Microsoft.AspNetCore.Identity;

var builder = WebApplication.CreateBuilder(args);
var basePath = AppContext.BaseDirectory;

//引入配置文件
var _config = new ConfigurationBuilder()
    .SetBasePath(basePath)
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
    .Build();
builder.Services.AddSingleton(new AppSettingsHelper(_config));

#region 接口分组

var groups = new List<Tuple<string, string>>
{
    //new Tuple<string, string>("Group1","分组一"),
    //new Tuple<string, string>("Group2","分组二")
};

#endregion

#region 注入数据库

var dbtype = DbType.SqlServer;
if (AppSettingsHelper.Get("SugarConnectDBType", true) == "mysql")
{
    dbtype = DbType.MySql;
}

builder.Services.AddSingleton(options =>
{
    return new SqlSugarScope(new List<ConnectionConfig>()
    {
        new ConnectionConfig()
        {
            ConfigId = DBEnum.Default, ConnectionString = AppSettingsHelper.Get("SugarConnectString", true),
            DbType = dbtype, IsAutoCloseConnection = true
        }
    });
});

#endregion

#region 初始化Redis

RedisHelper.Initialization(new CSRedisClient(AppSettingsHelper.Get("CSRedisConnectString", true)));

#endregion

#region 添加swagger注释

if (AppSettingsHelper.Get("UseSwagger").ToBool())
{
    builder.Services.AddSwaggerGen(a =>
    {
        a.SwaggerDoc("v1", new OpenApiInfo
        {
            Version = "v1",
            Title = "Api",
            Description = "Api接口文档"
        });
        foreach (var item in groups)
        {
            a.SwaggerDoc(item.Item1,
                new OpenApiInfo { Version = item.Item1, Title = item.Item2, Description = $"{item.Item2}接口文档" });
        }

        a.IncludeXmlComments(Path.Combine(basePath, "NET7.Api.xml"), true);
        a.IncludeXmlComments(Path.Combine(basePath, "NET7.Domain.xml"), true);
        a.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        {
            Description = "Value: Bearer {token}",
            Name = "Authorization",
            In = ParameterLocation.Header,
            Type = SecuritySchemeType.ApiKey,
            Scheme = "Bearer"
        });
        a.AddSecurityRequirement(new OpenApiSecurityRequirement()
        {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                    },
                    Scheme = "oauth2", Name = "Bearer", In = ParameterLocation.Header
                },
                new List<string>()
            }
        });
    });
}

#endregion

#region ======== 授权认证 ========

// 基于角色的授权 [Authorize(Roles = "admin,manager")]
// https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/roles?view=aspnetcore-7.0
builder.Services.AddIdentityCore<IdentityUser>().AddRoles<IdentityRole>();

var config = builder.Configuration;

// 添加身份验证服务
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
    // 方式一: 配置验证服务器
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true, //是否验证Issuer
        ValidateAudience = true, //是否验证Audience
        ValidateLifetime = true, //是否验证失效时间
        ValidateIssuerSigningKey = true, //是否验证SecurityKey
        ValidIssuer = config["Jwt:Issuer"], //发行机构
        ValidAudience = config["Jwt:Audience"], //接收者
        ClockSkew = TimeSpan.FromSeconds(30), //时间容错(解决服务器端时间不同步问题)
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:SecretKey"]!)),
        RequireExpirationTime = true,

        // 添加角色验证，在Action标记为[Authorize(Roles = "user,admin")]
        // NameClaimType = "name",
        // RoleClaimType = "role"
    };

    //监听JWT过期事件
    options.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = ctx =>
        {
            if (ctx.Exception.GetType() == typeof(SecurityTokenExpiredException))
                ctx.Response.Headers.Add("jwt_exception", "expired");
            return Task.CompletedTask;
        },
        OnChallenge = ctx =>
        {
            ctx.HandleResponse(); //Skips any default logic for this challenge
            ctx.Response.ContentType = "application/json";
            ctx.Response.StatusCode = StatusCodes.Status200OK;
            ctx.Response.WriteAsync(new { code = 401, msg = "访问未授权" }.ToJson());
            return Task.FromResult(0);
        },
        // OnMessageReceived = ctx =>
        // {
        //     
        //     //此处可以拦截Token，用于实现登出操作
        //     var token = ctx.HttpContext.Request.Headers["Authorization"].ToString();
        //     ctx.HttpContext.Request.Headers["Authorization"] = "";
        //     return Task.CompletedTask;
        // }
    };

    // 方式二: 指向其他身份验证服务器
    // options.Authority = "https://localhost:5001";
    // options.Audience = "api1";
    //https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/introduction?view=aspnetcore-7.0
});

// 添加授权服务
builder.Services.AddAuthorization(options =>
{
    //授权策略
    // [Authorize(Policy = "RoleGroup")]     //仅vip和内部员工(staff)可访问
    // [Authorize(Policy = "ManagerGroup")]  //仅1001、1002管理员组可以访问
    options.AddPolicy("RoleGroup", policy => policy.RequireRole("vip", "admin"));
    options.AddPolicy("ManagerGroup", policy => policy.RequireClaim(MyClaims.Group, "1001", "1002"));
});

#endregion

#region 初始化日志

builder.Host.UseSerilog((builderContext, config) =>
{
    config
        .MinimumLevel.Warning()
        .Enrich.FromLogContext()
        .WriteTo.Console()
        .WriteTo.File(Path.Combine("Logs", @"Log.txt"), rollingInterval: RollingInterval.Day);
});

#endregion

#region 允许服务器同步IO

builder.Services.Configure<KestrelServerOptions>(a => a.AllowSynchronousIO = true)
    .Configure<IISServerOptions>(a => a.AllowSynchronousIO = true);

#endregion

#region 初始化Autofac 注入程序集

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
var hostBuilder = builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
    var assembly = Assembly.Load("NET7.Infrastructure");
    builder.RegisterAssemblyTypes(assembly).Where(a => a.Name.EndsWith("Repository")).AsSelf();
});

#endregion

#region 初始化AutoMapper 自动映射

var serviceAssembly = Assembly.Load("NET7.Domain");
builder.Services.AddAutoMapper(serviceAssembly);

#endregion

#region 注入后台服务

builder.Services.AddHostedService<TimerService>();

#endregion

#region 注入事件总线

builder.Services.AddEventBus(builder =>
{
    builder.ChannelCapacity = 5000;
    builder.AddSubscriber<LogSubscriber>();
    builder.AddSubscriber<LoginSubscriber>();
    builder.AddSubscriber<OrderSubscriber>();
    builder.UnobservedTaskExceptionHandler = (obj, args) => { Log.Error($"事件总线异常：{args.Exception}"); };
    //builder.ReplaceStorer(serviceProvider =>
    //{
    //    return new RedisEventSourceStorer();
    //});
});

#endregion

#region 注入系统缓存

builder.Services.AddMemoryCache();

#endregion

#region 注入http上下文

builder.AddServiceProvider();

#endregion

#region 注入限流配置

builder.AddRateLimit();

#endregion

// Add services to the container.
builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<LogActionFilter>();
    options.Filters.Add<GlobalExceptionFilter>();
}).AddJsonOptions(options =>
{
    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
    options.JsonSerializerOptions.Converters.Add(new DatetimeJsonConverter());
    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
});

var app = builder.Build();

// Configure the HTTP request pipeline.

#region 启用静态资源访问

//创建目录
var path = Path.Combine(basePath, "Files/");
CommonFun.CreateDir(path);
//添加MIME支持
var provider = new FileExtensionContentTypeProvider();
provider.Mappings.Add(".fbx", "application/octet-stream");
provider.Mappings.Add(".obj", "application/octet-stream");
provider.Mappings.Add(".mtl", "application/octet-stream");
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(path),
    ContentTypeProvider = provider,
    RequestPath = "/Files"
});

#endregion

#region 启用跨域访问

app.UseCors(builder => builder
    .WithOrigins(AppSettingsHelper.Get("Origins"))
    .AllowCredentials()
    .AllowAnyMethod()
    .AllowAnyHeader());

#endregion

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.UseIpRateLimiting();

#region 启用swaggerUI

if (AppSettingsHelper.Get("UseSwagger").ToBool())
{
    app.UseSwagger();
    app.UseSwaggerUI(a =>
    {
        a.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
        foreach (var item in groups)
        {
            a.SwaggerEndpoint($"/swagger/{item.Item1}/swagger.json", item.Item2);
        }

        a.RoutePrefix = string.Empty;
        a.DocExpansion(DocExpansion.None);
        a.DefaultModelsExpandDepth(-1); //不显示Models
    });
}

#endregion

app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();